import { listenFromTopic, RabbitmqServer } from "@nodejs/mq";
import { Channel, Message } from "amqplib";
import { WebSocket } from "ws";
import { loadProto, requestFromBuffer, responseFromBuffer } from "./proto";
import { isErrorMessage, isRequestMessage, isResponseMessage, isSendToMessage, isSendToRoomMessage, registeOnDestoryHook, registerLifeCycle, RequestMessage } from "@core/types";
import { parseJSON } from "@tools/core";
import { useSecret, useToken } from "./utils";
import * as jwt from 'jsonwebtoken'
const globalMap: Map<string, Map<string, WebSocket>> = new Map()

export async function createWsRoom(room: string, handler: (msg: Message, channel: Channel, map: Map<string, WebSocket>, rootProto: protobuf.Root) => void) {
    let map: Map<string, WebSocket> = new Map()
    globalMap.set(room, map)
    const rabbitmqServer = new RabbitmqServer()
    const rootProto = await loadProto()
    await rabbitmqServer.onInit()
    registerLifeCycle(rabbitmqServer)
    await rabbitmqServer.createRoom(room, (msg, c) => handler(msg, c, map, rootProto));
    return {
        add: (uuid: string, ws: WebSocket) => map.set(uuid, ws),
        remove: (uuid: string) => map.delete(uuid)
    }
}
/**
 * 接收消息
 */
export async function createResponseRoom() {
    return createWsRoom('ws.room.response', (msg, channel, map, rootProto) => {
        try {
            const response = responseFromBuffer(rootProto, msg.content)
            if (isErrorMessage(response)) {
                const payload = response.payload
                const ws = map.get(payload.reqid)
                if (!ws) return channel.ack(msg)
                ws.send(JSON.stringify({ type: response.type, message: payload.message, stack: payload.stack, data: parseJSON(payload.data.toString()) }))
                channel.ack(msg)
                return;
            }
            if (isSendToMessage(response)) {
                const payload = response.payload
                const ws = map.get(payload.to)
                if (!ws) return channel.ack(msg)
                ws.send(payload.data.toString())
                channel.ack(msg)
                return;
            }
            // 群发
            if (isSendToRoomMessage(response)) {
                const payload = response.payload
                const room = globalMap.get(payload.roomid)
                if (!room) return channel.ack(msg)
                // 判断是否已经发送过了
                room.forEach((ws) => {
                    ws.send(payload.data.toString())
                });
                channel.ack(msg)
                return;
            }
            if (isResponseMessage(response)) {
                const payload = response.payload
                const ws = map.get(payload.reqid)
                if (!ws) return channel.ack(msg)
                ws.send(payload.data.toString())
                channel.ack(msg)
                return;
            }
            channel.ack(msg);
        } catch (e) {
            channel.ack(msg)
        }
    })
}

/**
 * 发送消息
 */

const requestWsMap: Map<string, WebSocket> = new Map()
export async function createRequestRoom(name: string, uuid: string) {
    if (requestWsMap.has(uuid)) return requestWsMap
    const rootProto = await loadProto()
    await listenFromTopic('request.topic', `ws.request.${uuid}`, `request.${name}.${uuid}`, async (msg, channel) => {
        try {
            const request = requestFromBuffer(rootProto, msg.content)
            if (isRequestMessage(request)) {
                const payload = request.payload
                const data = parseJSON(payload.data.toString('utf8'))
                const headers = parseJSON(payload.headers.toString('utf8'))
                const token = useToken(data, headers)
                const path = payload.path;
                const method = payload.method;
                const name = payload.name;
                const appid = payload.appid;
                const reqid = payload.reqid;
                const ip = payload.ip;
                const version = payload.version;
                let user: any = null;
                try {
                    const secret = useSecret()
                    user = jwt.verify(token, secret)
                } catch (e) { }
                const ws = requestWsMap.get(uuid)
                if (!ws) {
                    console.log(`任务已下发失败，ws not found ${uuid}`)
                    return;
                }
                console.log(`任务已下发，正在处理中...`)
                ws.send(JSON.stringify({
                    type: 'REQUEST',
                    payload: {
                        data,
                        headers,
                        user,
                        path,
                        method,
                        name,
                        appid,
                        reqid,
                        ip,
                        version
                    }
                }));
            }
        } catch (e) { 
            console.error(e)
        } finally {
            channel.ack(msg)
        }
    }, {
        durable: true
    })
    return requestWsMap;
}

